﻿using System;
using System.Windows.Input;
using Microsoft.Practices.Prism.Commands;

namespace Mitosoft.Definitions.ViewModels
{
  /// <summary>
  /// View model containing zoom logic.
  /// </summary>
  public class ZoomViewModel : BaseViewModel
  {
    #region fields

    /// <summary>
    /// Represents <see cref="ZoomFactor"/>
    /// </summary>
    protected double _zoomFactor = 1.0;

    /// <summary>
    /// Represents <see cref="ZoomDownCommand"/>
    /// </summary>
    protected ICommand _zoomDownCommand;

    /// <summary>
    /// Represents <see cref="ZoomUpCommand"/>
    /// </summary>
    protected ICommand _zoomUpCommand;

    /// <summary>
    /// Represents <see cref="ZoomTo100PercentCommand"/>
    /// </summary>
    protected ICommand _zoomTo100PercentCommand;

    #endregion

    #region constructors
    #endregion

    #region properties

    /// <summary>
    /// Command used to zoom to 100 percent.
    /// </summary>
    public ICommand ZoomTo100PercentCommand
    {
      get
      {
        return
          _zoomTo100PercentCommand ??
            (_zoomTo100PercentCommand =
              new DelegateCommand(
                OnZoomTo100PercentCommandExecuted,
                CanExecuteZoomTo100PercentCommand));
      }
    }

    /// <summary>
    /// Represents the command used to zoom up.
    /// </summary>
    public ICommand ZoomUpCommand
    {
      get
      {
        return
          _zoomUpCommand ??
               (_zoomUpCommand =
                  new DelegateCommand(
                     OnZoomUpCommandExecuted,
                     CanExecuteZoomUpCommand));
      }
    }

    /// <summary>
    /// Represents the command used to zoom down.
    /// </summary>
    public ICommand ZoomDownCommand
    {
      get
      {
        return
          _zoomDownCommand ??
               (_zoomDownCommand =
                  new DelegateCommand(
                    OnZoomDownCommandExecuted,
                    CanExecuteZoomDownCommand));
      }
    }

    /// <summary>
    /// Gets or sets the zoom factor.
    /// </summary>
    public double ZoomFactor
    {
      get { return _zoomFactor; }
      set
      {
        if (Math.Abs(value - _zoomFactor) < 0.001)
          return;

        _zoomFactor = value;
        HandlePropertyChanged();
        ((DelegateCommand)ZoomTo100PercentCommand).RaiseCanExecuteChanged();
      }
    }

    /// <summary>
    /// Represents the maximum zoom factor value.
    /// </summary>
    public double MaxZoomFactor
    {
      get { return 5.0; }
    }

    /// <summary>
    /// Represents the minimum zoom factor value.
    /// </summary>
    public double MinZoomFactor
    {
      get { return 0.5; }
    }

    #endregion

    #region methods

    /// <summary>
    /// Disposes this object.
    /// </summary>
    /// <param name="isDisposing">Represents whether this object is disposing.</param>
    protected override void Dispose(bool isDisposing)
    {
      if (_isDisposed) return;

      if (isDisposing)
      {
        #region dispose managed resources

        _zoomDownCommand = null;
        _zoomUpCommand = null;
        _zoomTo100PercentCommand = null;

        #endregion
      }

      #region clean up unmanaged resources

      #endregion

      _isDisposed = true;
    }

    /// <summary>
    /// Command <see cref="ZoomTo100PercentCommand"/> shall be executed.
    /// </summary>
    protected void OnZoomTo100PercentCommandExecuted()
    {
      ZoomFactor = 1.0;
    }

    /// <summary>
    /// Returns whether <see cref="ZoomTo100PercentCommand"/> can be executed.
    /// </summary>
    /// <returns>True if <see cref="ZoomTo100PercentCommand"/> can be executed.</returns>
    protected bool CanExecuteZoomTo100PercentCommand()
    {
      // ReSharper disable once CompareOfFloatsByEqualityOperator
      return Math.Round(ZoomFactor, 1) != 1.0;
    }

    /// <summary>
    /// Returns whether <see cref="ZoomUpCommand"/> can be executed.
    /// </summary>
    /// <returns>True if <see cref="ZoomUpCommand"/> can be executed.</returns>
    protected bool CanExecuteZoomUpCommand()
    {
      return ZoomFactor < MaxZoomFactor;
    }

    /// <summary>
    /// Command <see cref="ZoomUpCommand"/> shall be executed.
    /// </summary>
    protected void OnZoomUpCommandExecuted()
    {
      double newZoomFactor = ZoomFactor;

      if (newZoomFactor > 0)
        newZoomFactor += 0.1;
      else
        newZoomFactor += 0.01;

      newZoomFactor = Math.Min(MaxZoomFactor, newZoomFactor);

      //Debug.Print("{0} {1} {2} {3}", "ZoomFactor will change from ", ZoomFactor, "to", newZoomFactor);

      ZoomFactor = newZoomFactor;
    }

    /// <summary>
    /// Returns whether <see cref="ZoomDownCommand"/> can be executed.
    /// </summary>
    /// <returns>True if <see cref="ZoomDownCommand"/> can be executed.</returns>
    protected bool CanExecuteZoomDownCommand()
    {
      return ZoomFactor > MinZoomFactor;
    }

    /// <summary>
    /// Command <see cref="ZoomDownCommand"/> shall be executed.
    /// </summary>
    protected void OnZoomDownCommandExecuted()
    {
      double newZoomFactor = ZoomFactor;

      if (newZoomFactor > 0)
        newZoomFactor -= 0.1;
      else
        newZoomFactor -= 0.01;

      newZoomFactor = Math.Max(MinZoomFactor, newZoomFactor);

      //Debug.Print("{0} {1} {2} {3}", "ZoomFactor will change from ", ZoomFactor, "to", newZoomFactor);

      ZoomFactor = newZoomFactor;
    }

    #endregion
  }
}
